package com.springmvc.servlet;

import cn.hutool.json.JSONUtil;
import com.springmvc.anotation.Controller;
import com.springmvc.anotation.RequestMapping;
import com.springmvc.anotation.RequestParam;
import com.springmvc.anotation.ResponseBody;
import com.springmvc.context.WebApplicationContext;
import com.springmvc.exception.ContextException;
import com.springmvc.handler.MyHandler;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * SpringMvc核心控制器
 */
public class DispatcherServlet extends HttpServlet {

    private WebApplicationContext webApplicationContext;
    // 存储URL和对象方法的映射关系
    private List<MyHandler> handlerList = new ArrayList<>();

    @Override
    public void init() throws ServletException {
        // Servlet初始化的时候，读取配置参数，classpath:springmvc.xml
        String contextConfigLocation = this.getServletConfig().getInitParameter("contextConfigLocation");
        // 创建容器
        webApplicationContext = new WebApplicationContext(contextConfigLocation);
        // 初始化容器
        webApplicationContext.refresh();
        // 初始化请求映射
        initHandleMapping();
        System.out.println("请求地址和控制器方法的映射：" + handlerList);

    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doGet(req, resp);

    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 请求分发
        excuteDispatch(req, resp);
    }

    /**
     * 初始化请求映射
     */
    private void initHandleMapping(){
        if (webApplicationContext.iocMap.isEmpty()){
            throw new ContextException("Spring容器为空");
        }

        for (Map.Entry<String, Object> iocEntry : webApplicationContext.iocMap.entrySet()) {
            // 取出ioc容器中bean的class对象
            Class<?> aClass = iocEntry.getValue().getClass();
            if(aClass.isAnnotationPresent(Controller.class)){
                // 含有controller注解，就取出所有的方法
                Method[] methods = aClass.getMethods();
                for (Method method : methods) {
                    if (method.isAnnotationPresent(RequestMapping.class)){
                        // 方法上含有RequestMapping注解
                        RequestMapping annotation = method.getAnnotation(RequestMapping.class);
                        String url = annotation.value();
                        handlerList.add(new MyHandler(url, iocEntry.getValue(), method));
                    }
                }
            }
        }


    }


    /**
     * 请求的分发
     */
    private void excuteDispatch(HttpServletRequest req, HttpServletResponse resp){
        MyHandler handler = getHandler(req);
        try {
            if (handler == null){
                resp.getWriter().print("<h1>404 Not Found</h1>");
                return;
            }
            // 获取方法的参数
            Class<?>[] parameterTypes = handler.getMethod().getParameterTypes();
            // 定义一个参数集合
            Object[] objects = new Object[parameterTypes.length];
            // 处理请求中的HttpRequest和HttpResponse
            for(int i = 0; i < parameterTypes.length; i++){
                Class<?> parameterType = parameterTypes[i];
                // 此处应该根据类型来判断请求
                if (parameterType.getSimpleName().equals("HttpServletRequest")){
                    objects[i] = req;
                }
                else if (parameterType.getSimpleName().equals("HttpServletResponse")){
                    objects[i] = resp;
                }
            }

            // 获取请求中的参数
            Map<String, String[]> parameterMap = req.getParameterMap();
            for (Map.Entry<String, String[]> paramItem : parameterMap.entrySet()) {
                String value = paramItem.getValue()[0];
                String key = paramItem.getKey();
                // 看参数是否有RequestParam注解
                int i = hasRequestParam(handler.getMethod(), key);
                if (i != -1){
                    objects[i] = value;
                }
                else{
                    // 无注解
                    // 获取所有参数列表，看是否存在参数
                    List<String> paramsName = getParamsName(handler.getMethod());
                    for(int j = 0; j < paramsName.size(); j ++){
                        if (paramsName.get(j).equals(key)){
                            objects[j] = value;
                            break;
                        }
                    }
                }

            }

            // 调用控制器的方法
            Object invoke = handler.getMethod().invoke(handler.getController(), objects);

            // 字符串处理
            if (invoke instanceof String){
                String view = (String) invoke;
                // 判断是否是转发或者重定向
                if (view.contains(":")){
                    String viewType = view.split(":")[0];
                    String viewValue = view.split(":")[1];
                    if (viewType.equals("forward")){
                        // 转发
                        req.getRequestDispatcher(viewValue).forward(req, resp);
                    }
                    else {
                        // 重定向
                        resp.sendRedirect(viewValue);
                    }
                } else{
                    // 默认转发【Spring默认就是转发】
                    req.getRequestDispatcher(view).forward(req, resp);
                }
            }
            else{
                // 返回json
                Method method = handler.getMethod();
                if (method.isAnnotationPresent(ResponseBody.class)){
                    String json = JSONUtil.toJsonStr(invoke);
                    resp.setContentType("application/json;charset=UTF-8");
                    PrintWriter printWriter = resp.getWriter();
                    printWriter.print(json);
                    printWriter.flush();
                    printWriter.close();
                }
            }

        } catch (Exception e){
            e.printStackTrace();
        }
    }


    /**
     * 根据请求获取MyHandler对象
     * @param req
     * @return
     */
    private MyHandler getHandler(HttpServletRequest req){
        String url = req.getRequestURI();
        List<MyHandler> collect = handlerList.stream().filter(item -> item.getUrl().equals(url)).collect(Collectors.toList());
        if (collect.size() == 0){
            return null;
        }
        return collect.get(0);
    }


    /**
     * 判断方法中是否有RequestParam参数，且参数是对于的value，返回对于下标
     * @param method
     * @param name
     * @return
     */
    private int hasRequestParam(Method method, String name){
        Parameter[] parameterTypes = method.getParameters();
        for (int i = 0; i < parameterTypes.length; i++){
            Parameter parameterType = parameterTypes[i];
            if (parameterType.isAnnotationPresent(RequestParam.class)){
                RequestParam requestParam = parameterType.getAnnotation(RequestParam.class);
                String value = requestParam.value();
                if (value.equals(name)){
                    return i;
                }
            }
        }
        return -1;
    }


    /**
     * 获取控制器参数名字列表
     * @param method
     * @return
     */
    private List<String> getParamsName(Method method){
        List<String> res = new ArrayList<>();
        Parameter[] parameters = method.getParameters();
        for (Parameter parameter : parameters) {
            res.add(parameter.getName());
        }
        return res;
    }
}
